const drawRope= ({canvas, ropeImageUrl, pwArr, drawWidth,needRadius=false,average=true,radius=10,unitLength=2,bezierNum=100,numPointsScale=1,showControlPoint=false})=> {
    loadAndDoStr(ropeImageUrl, function(ropeImage){
        var pointsObj = getPointByWaypoints(pwArr,bezierNum,numPointsScale,unitLength,needRadius,radius);
        var points = getSomeLengthPoint(pointsObj,unitLength,average?1:0);
        var bezierArr = pointsObj.bezierArr;
        var ctx = canvas.getContext("2d");

            var ropeWidth = ropeImage.width;
            var bl = drawWidth / ropeWidth;
            var ropeHeight = ropeImage.height;
            var usedWidth = drawWidth;

            for (var i = 0; i < points.length; i++) {
                var offX1 = points[i].x + Math.cos(points[i].angle);
                var offY1 = points[i].y + Math.sin(points[i].angle);
                var drawHeight = unitLength;

                ctx.translate(offX1, offY1); // 移动画布
                //这里角度要取下一个点的角度，这样是因为绘制的时倻，是根据下一个点的角度来绘制的
                ctx.rotate(points[i].angle); // 旋转画布
                //查看下一点的坐标角度跟当前角度相比是否变化过大
             
                // 绘制图像片段，如果截取长度超过图片长度，会有一段空白
                ctx.drawImage(ropeImage, 
                    0, 
                    ((i)*(drawHeight/bl))%ropeHeight, //剪切起点
                    // (((i-1)/bl )% ropeHeight), //剪切起点
                    // ((i-1)*(drawHeight/bl))%ropeHeight, //剪切起点
                    ropeWidth, (drawHeight/bl), //剪切终点
                    // 0,0,90,10,
                    -usedWidth / 2, 0, //绘制起点
                    usedWidth, drawHeight*4,//绘制终点
                );
                ctx.setTransform(1, 0, 0, 1, 0, 0); // 重置变换
            }

            //尝试绘制控制点
            
            showControlPoint&&bezierArr.map((item,idx)=>{
                //首尾单色，其他的每两个一组同色,根据idx变化
                var color ="";
                if(idx==0||idx==bezierArr.length-1){
                    color = "red";
                }else{
                    //根据idx生成颜色，跨度要大，结合bezierArr长度
                    var num = Math.floor((idx/(bezierArr.length-1))*255);
                    color =` rgb(${255-num},${num},${num})`;
                }
                ctx.beginPath();
                ctx.arc(item.x, item.y, 5, 0, 2 * Math.PI);
                ctx.fillStyle = color;
                ctx.fill();

            });

            //绘制分割点
            showControlPoint&&points.map((item,idx)=>{
                //首尾单色，其他的每两个一组同色,根据idx变化
                var color ="";
                if(idx==0||idx==points.length-1){
                    color = "blue";
                }
                ctx.beginPath();
                ctx.arc(item.x, item.y, 1, 0, 2 * Math.PI);
                ctx.fillStyle = color;
                ctx.fill();

            });
    });
}

//加载图片，并执行
function loadAndDoStr(imgSrc,cb) {
    if(!window["drawRopeObj"]){window["drawRopeObj"] = {};}
    if(window["drawRopeObj"][imgSrc]){
        cb(window["drawRopeObj"][imgSrc]);
        return;
    }else{
        var ropeImage = new Image();
        ropeImage.src = imgSrc;
        console.log(ropeImage);
        ropeImage.onload = function() {
            window["drawRopeObj"][imgSrc] = ropeImage;
            cb(ropeImage);
        }
    }
}

//根据弧长筛选等距点位
function getSomeLengthPoint(pointsObj,unitLength,type=1){
    // type = 1;//0:自然分布 1:等距均分
    console.log(type,"type")
    var arr = [];//所有点位
    var length = 0;
    var points = pointsObj.arr;
    var lines = pointsObj.linesObj.lines;
    if(type==0){
        //自然分布
        for(var i = 0; i < points.length - 1; i++){
            var start = points[i];
            var end = points[i + 1];
            var dis = Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2));
            length += dis;
            if(i==0){
                arr.push(start);
            }else if(length >= unitLength){
                arr.push(start);
                length = 0;
            }
        }
        arr.push(points[points.length - 1]);
    }else{
        //等距均分
        var tValues = findTForLength(lines, unitLength);
        // console.log(tValues,"tValues")
        tValues.map((item,idx)=>{
            item.map((t)=>{
                if(t==0){
                    //起点,这样避免起点切线角度为0
                    t = 0.0001;
                }
                var point = getCubicBezierXYatT(lines[idx].ctlPoint[0], lines[idx].ctlPoint[1], lines[idx].ctlPoint[2], lines[idx].ctlPoint[3], t);
                //切线
                var tx = bezierTangent(lines[idx].ctlPoint[0].x, lines[idx].ctlPoint[1].x, lines[idx].ctlPoint[2].x, lines[idx].ctlPoint[3].x, t);
                var ty = bezierTangent(lines[idx].ctlPoint[0].y, lines[idx].ctlPoint[1].y, lines[idx].ctlPoint[2].y, lines[idx].ctlPoint[3].y, t);
                //角度，原理是切线角度-90度，因为图片是竖直的
                var angle = Math.atan2(ty, tx) - Math.PI / 2;
                arr.push({x:point.x,y:point.y,angle:angle});
            })
        })
    }

    //计算点位之间的距离
    arr.map((item,idx)=>{
        if(idx!=0){
            var dis = Math.sqrt(Math.pow(item.x - arr[idx-1].x, 2) + Math.pow(item.y - arr[idx-1].y, 2));
        }
    })
    return arr;
}

function findTForLength(lines, unitLength) {
    const result = [];
    let moreLength = 0;
    for (let i = 0; i < lines.length; i++) {
        result.push([]);
        var num = Math.floor(lines[i].length/unitLength);
        if(moreLength<(lines[i].length % unitLength)&&moreLength!=0){
            num+=1
        }
        for(var j = 0; j <= num; j++){
            var targetLength =  (unitLength*j)+moreLength;
            let { low, high} = binarySearchLength(lines[i].lengthArr, targetLength);
            if((lines[i].lengthArr.length-1) >=high){
                const t = interpolateT(lines[i].lengthArr, low, high, targetLength);
                if(t){
                    result[i].push(t);
                }
            }
         
        }
        moreLength = unitLength-(lines[i].length % unitLength);
    }

    return result;
}

function interpolateT(lengthArr, low, high, targetLength) {
    
    let lowPoint = lengthArr[low];
    let highPoint = lengthArr[high];
    const ratio = (targetLength - lowPoint.l) / (highPoint.l - lowPoint.l);
    const t = lowPoint.t + ratio * (highPoint.t - lowPoint.t);
    return t;
}

//使用二分查找找到目标弧长在 lengthArr 中的位置。
function binarySearchLength(lengthArr, targetLength) {
    let low = 0;
    let high = lengthArr.length - 1;
    if (targetLength==0) {
        return { low:0, high:1 };
    }
    while (low <= high) {
        const mid = Math.floor((low + high) / 2);
        const length = lengthArr[mid].l;

        if (length < targetLength) {
            low = mid + 1;
        } else if (length > targetLength) {
            high = mid - 1;
        } else {
            return { low, high };
        }
    }
    if (low > high) {
        var num = low;
        low = high;
        high = num;
    }

    return { low, high };
}



//传入两点，从终点开始的距离，求该直线上的该距离的点的坐标
function getPointByDistance(start, end, distance) {
    var x = end.x - start.x;
    var y = end.y - start.y;
    var z = Math.sqrt(x * x + y * y);
    var xie = x / z;
    var yie = y / z;
    return { x: end.x - xie * distance, y: end.y - yie * distance };
}


function calculateBezierPoints(start, control1, control2, end, numPoints) {
    var points = [];
    var length = 0;
    var lengthArr = [];
    for (var t = 0; t <= numPoints; t++) {
        var T = t / numPoints;
        // 计算曲线上的点
        var pos = getCubicBezierXYatT(start, control1, control2, end, T);
        // 计算曲线在该点的切线角度
        var tx = bezierTangent(start.x, control1.x, control2.x, end.x, T);
        var ty = bezierTangent(start.y, control1.y, control2.y, end.y, T);
        var angle = Math.atan2(ty, tx) - Math.PI / 2;
        points.push({ x: pos.x, y: pos.y, angle: angle });
        var _length = 0;
        if(t>0){
             _length = Math.sqrt(Math.pow(pos.x - points[t-1].x, 2) + Math.pow(pos.y - points[t-1].y, 2));
        }
        length += _length;
        lengthArr.push({l:length,t:T});
    }
    return {
        points: points,
        length: length,
        lengthArr:lengthArr
    };
}

// calculate one XY point along Cubic Bezier at interval T
// (where T==0.00 at the start of the curve and T==1.00 at the end)
function getCubicBezierXYatT(startPt, controlPt1, controlPt2, endPt, T) {
    var x = CubicN(T, startPt.x, controlPt1.x, controlPt2.x, endPt.x);
    var y = CubicN(T, startPt.y, controlPt1.y, controlPt2.y, endPt.y);
    return { x: x, y: y };
}

// cubic helper formula at T distance
//贝塞尔曲线公式 三次方程，这里返回的点位不够平均
function CubicN(T, a, b, c, d) {
    var t2 = T * T;
    var t3 = t2 * T;
    return a + (-a * 3 + T * (3 * a - a * T)) * T + (3 * b + T * (-6 * b + b * 3 * T)) * T + (c * 3 - c * 3 * T) * t2 + d * t3;
}

// calculate the tangent angle at interval T on the curve
function bezierTangent(a, b, c, d, t) {
    return (3 * t * t * (-a + 3 * b - 3 * c + d) + 6 * t * (a - 2 * b + c) + 3 * (-a + b));
};



//根据途径点算出所有分割点
function getPointByWaypoints(pwArr,bezierNum,numPointsScale,unitLength,needRadius,radius){
    var arr = [];
    var bezierArr = [];//控制点Arr
    var bezierChangeNum = bezierNum;
    var linesObj= {
        length:0,
        lines:[]
    };
    //分段数量.越大越平滑,默认400
    if(!needRadius){
        //非圆角模式
        for(var i = 0; i < pwArr.length - 1; i++){
            var start = pwArr[i];
            var end = pwArr[i + 1];
            var control0,control1,control2;
            var line = {
                length:0,
                ctlPoint:null,
                lengthArr:null,
                points:null
            }
            if(i==0){
                //起点
                control0 = start;
                bezierArr.push(control0)
            }
            if(i<pwArr.length-2){
                var p_obj = getControlPoint(start, end, pwArr[i + 2], bezierChangeNum);//计算控制点
                control1 = p_obj.control1;
                control2 = p_obj.control2;
                bezierArr.push(control1,control2)
            }else if(i==pwArr.length-2){
                var lastPoint = bezierArr[bezierArr.length-1];
                var endPonint = getPointByDistance(lastPoint,end,bezierChangeNum/2)
                bezierArr.push(endPonint)
            }
            //numPoints 根据两点距离计算分段
            var numPoints = Math.floor(Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2))*numPointsScale);
            var {points,length,lengthArr} = calculateBezierPoints(start, bezierArr[i*2], bezierArr[(i*2)+1], end,numPoints);
            line.length = length;
            line.ctlPoint = [start,bezierArr[i*2],bezierArr[(i*2)+1],end];
            line.points = points;
            line.lengthArr = lengthArr;
            linesObj.lines.push(line);
            linesObj.length += length;
            arr = arr.concat(points);
        }
    }else{
        //圆角模式
        var {pArr,controlArr} = getRightPointRadius(pwArr,radius)//圆角修正后的点位
        bezierArr = controlArr;
        for(var i = 0; i < pArr.length - 1; i++){
            var line = {
                length:0,
                point:null
            }
            var start = pArr[i];
            var end = pArr[i + 1];
            var numPoints = Math.floor(Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2))*numPointsScale);
            var {points,length,lengthArr} = calculateBezierPoints(start, bezierArr[i*2], bezierArr[(i*2)+1], end,numPoints);
            line.length = length;
            line.ctlPoint = [start,bezierArr[i*2],bezierArr[(i*2)+1],end];
            line.points = points;
            line.lengthArr = lengthArr;
            linesObj.lines.push(line);
            linesObj.length += length;
            arr = arr.concat(points);
        }
    }
    
    return {
        arr:arr,
        bezierArr:bezierArr,
        linesObj:linesObj
    };
}

//根据当前点位重新计算圆角途径点
function getRightPointRadius(pwArr,radius) {
    // 计算前点到后点的向量
    var pArr = [];
    var controlArr = [];
    for(var i = 0; i < pwArr.length; i++){
        if(i==0||i==pwArr.length-1){
            var p = pwArr[i];
            var p_r = {};
            var c_length = 0;
            if(i==0){
                c_length = Math.sqrt(Math.pow(pwArr[i+1].x-p.x,2)+Math.pow(pwArr[i+1].y-p.y,2))/3;
                p_r = getPointByDistance(pwArr[i+1],p,c_length);
            }else{
                c_length = Math.sqrt(Math.pow(pwArr[i-1].x-p.x,2)+Math.pow(pwArr[i-1].y-p.y,2))/3;
                p_r = getPointByDistance(pwArr[i-1],p,c_length);
            }
            pArr.push(pwArr[i]);
            controlArr.push(p_r);
        }else{
            var p = pwArr[i];
            var pPre = pwArr[i-1];
            var pNext = pwArr[i+1];

            var p1 =  getPointByDistance(pPre,p,radius);
            var p2 =  getPointByDistance(pNext,p,radius);
            var p1_r =  getPointByDistance(pPre,p1,radius/2);
            var p1_1_r =  getPointByDistance(p,p1,radius/2);
            var p2_r =  getPointByDistance(p,p2,radius/2);
            var p2_1_r =  getPointByDistance(pNext,p2,radius/2);
            pArr.push(p1,p2);
            controlArr.push(p1_r,p1_1_r,p2_r,p2_1_r);
        }
    }
    return {pArr,controlArr};
}

 //根据当前点位的前一点和后一点，计算当前点的控制点
 function getControlPoint(pre, cur, next, bezierNum) {
    // 计算前点到后点的向量
    var dx = next.x - pre.x;
    var dy = next.y - pre.y;
    var length = Math.sqrt(dx * dx + dy * dy);

    // 计算单位向量
    var unitDx = dx / length;
    var unitDy = dy / length;

    //计算前点到当前点的距离
    var dis = Math.sqrt((cur.x - pre.x) * (cur.x - pre.x) + (cur.y - pre.y) * (cur.y - pre.y));
    //计算后点到当前点的距离
    var dis2 = Math.sqrt((cur.x - next.x) * (cur.x - next.x) + (cur.y - next.y) * (cur.y - next.y));
    var bezierNum_q = bezierNum*(dis/(dis+dis2));
    var bezierNum_h = bezierNum - bezierNum_q;

    // 根据单位向量和bezierNum计算控制点
    var control1 = {
        x: cur.x - unitDx * bezierNum_q,
        y: cur.y - unitDy * bezierNum_q
    };

    var control2 = {
        x: cur.x + unitDx * bezierNum_h,
        y: cur.y + unitDy * bezierNum_h
    };

    return { control1, control2 };
}

export {drawRope};